package com.fount4j.generator.introspect;

import com.fount4j.generator.context.GeneratorContext;
import com.fount4j.generator.exception.GenerateException;
import com.fount4j.generator.info.ColumnInfo;
import com.fount4j.generator.util.StringUtils;

import java.math.BigDecimal;
import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 列解析器
 *
 * @author Morven 2016/12/22
 */
public class ColumnIntrospector {
    /** 数据库列的类型与 MyBatis jdbcType 和实体类属性类型的对应关系 */
    private Map<Integer, JdbcTypeInformation> typeMap = new HashMap<>();

    public ColumnIntrospector() {
        typeMap.put(Types.ARRAY, new JdbcTypeInformation("ARRAY", Object.class));
        typeMap.put(Types.BIGINT, new JdbcTypeInformation("BIGINT", Long.class));
        typeMap.put(Types.BINARY, new JdbcTypeInformation("BINARY", byte[].class));
        typeMap.put(Types.BIT, new JdbcTypeInformation("BIT", Boolean.class));
        typeMap.put(Types.BLOB, new JdbcTypeInformation("BLOB", byte[].class));
        typeMap.put(Types.BOOLEAN, new JdbcTypeInformation("BOOLEAN", Boolean.class));
        typeMap.put(Types.CHAR, new JdbcTypeInformation("CHAR", String.class));
        typeMap.put(Types.CLOB, new JdbcTypeInformation("CLOB", String.class));
        typeMap.put(Types.DATALINK, new JdbcTypeInformation("DATALINK", Object.class));
        typeMap.put(Types.DATE, new JdbcTypeInformation("DATE", Date.class));
        typeMap.put(Types.DECIMAL, new JdbcTypeInformation("DECIMAL", BigDecimal.class));
        typeMap.put(Types.DISTINCT, new JdbcTypeInformation("DISTINCT", Object.class));
        typeMap.put(Types.DOUBLE, new JdbcTypeInformation("DOUBLE", Double.class));
        typeMap.put(Types.FLOAT, new JdbcTypeInformation("FLOAT", Double.class));
        typeMap.put(Types.INTEGER, new JdbcTypeInformation("INTEGER", Integer.class));
        typeMap.put(Types.JAVA_OBJECT, new JdbcTypeInformation("JAVA_OBJECT", Object.class));
        typeMap.put(Types.LONGNVARCHAR, new JdbcTypeInformation("LONGNVARCHAR", String.class));
        typeMap.put(Types.LONGVARBINARY, new JdbcTypeInformation("LONGVARBINARY", byte[].class));
        typeMap.put(Types.LONGVARCHAR, new JdbcTypeInformation("LONGVARCHAR", String.class));
        typeMap.put(Types.NCHAR, new JdbcTypeInformation("NCHAR", String.class));
        typeMap.put(Types.NCLOB, new JdbcTypeInformation("NCLOB", String.class));
        typeMap.put(Types.NVARCHAR, new JdbcTypeInformation("NVARCHAR", String.class));
        typeMap.put(Types.NULL, new JdbcTypeInformation("NULL", Object.class));
        typeMap.put(Types.NUMERIC, new JdbcTypeInformation("NUMERIC", BigDecimal.class));
        typeMap.put(Types.OTHER, new JdbcTypeInformation("OTHER", Object.class));
        typeMap.put(Types.REAL, new JdbcTypeInformation("REAL", Float.class));
        typeMap.put(Types.REF, new JdbcTypeInformation("REF", Object.class));
        typeMap.put(Types.SMALLINT, new JdbcTypeInformation("SMALLINT", Short.class));
        typeMap.put(Types.STRUCT, new JdbcTypeInformation("STRUCT", Object.class));
        typeMap.put(Types.TIME, new JdbcTypeInformation("TIME", Date.class));
        typeMap.put(Types.TIMESTAMP, new JdbcTypeInformation("TIMESTAMP", Date.class));
        typeMap.put(Types.TINYINT, new JdbcTypeInformation("TINYINT", Byte.class));
        typeMap.put(Types.VARBINARY, new JdbcTypeInformation("VARBINARY", byte[].class));
        typeMap.put(Types.VARCHAR, new JdbcTypeInformation("VARCHAR", String.class));
    }

    /**
     * 解析列
     *
     * @param context    生成器上下文
     * @param columnInfo 要解析的列
     */
    public void introspectColumn(GeneratorContext context, ColumnInfo columnInfo) {
        introspectPropertyName(context, columnInfo);
        introspectJdbcType(context, columnInfo);
        introspectLenScale(context, columnInfo);
        introspectNotEmptyExp(context, columnInfo);
    }

    /**
     * 列不为空的判断表达式
     *
     * @param context    生成器上下文
     * @param columnInfo 要解析的列
     */
    protected void introspectNotEmptyExp(GeneratorContext context, ColumnInfo columnInfo) {
        if (String.class.equals(columnInfo.getJavaType())) {
            columnInfo.setNotEmptyExp(" and " + columnInfo.getJavaName() + " != ''");
        }
    }

    /**
     * 解析列名
     *
     * @param context    生成器上下文
     * @param columnInfo 要解析的列
     */
    protected void introspectPropertyName(GeneratorContext context, ColumnInfo columnInfo) {
        String propertyName = StringUtils.lineToHump(columnInfo.getRealName());
        String propertyUpper = StringUtils.firstUpper(propertyName);
        columnInfo.setJavaName(propertyName);
        columnInfo.setJavaNameUpper(propertyUpper);
    }

    /**
     * 解析字段的长度和精确度的描述
     *
     * @param context    生成器上下文
     * @param columnInfo 要解析的列
     */
    protected void introspectLenScale(GeneratorContext context, ColumnInfo columnInfo) {
        StringBuilder sb = new StringBuilder();
        if (columnInfo.getLen() > 0) {
            sb.append("(").append(columnInfo.getLen());
        } else if (columnInfo.getScale() > 0) {
            sb.append("(0");
        }
        if (columnInfo.getScale() > 0) {
            sb.append(",").append(columnInfo.getScale());
        }
        if (sb.length() > 0) {
            sb.append(")");
        }
        columnInfo.setLenScale(sb.toString());
    }

    /**
     * 解析列的类型
     *
     * @param context    生成器上下文
     * @param columnInfo 要解析的列
     */
    protected void introspectJdbcType(GeneratorContext context, ColumnInfo columnInfo) {
        JdbcTypeInformation jdbcTypeInformation = typeMap.get(columnInfo.getDbType());
        if (jdbcTypeInformation == null) {
            throw new GenerateException("未知的列类型: " + columnInfo.getDbTypeName());
        }
        columnInfo.setJdbcType(jdbcTypeInformation.getJdbcType());
        columnInfo.setJavaType(jdbcTypeInformation.getJavaType());
        if (jdbcTypeInformation.getJavaType().equals(byte[].class)) {
            columnInfo.setJavaTypeName("byte[]");
        } else {
            columnInfo.setJavaTypeName(jdbcTypeInformation.getJavaType().getSimpleName());
        }
    }
}
